package com.qf.ticketredis.controller;

import com.qf.ticketredis.pojo.Ticket;
import com.qf.ticketredis.service.TicketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/ticket")
public class TicketController2 {

	public static final String TICKET_LOCK_PREFIX = "ticket:lock:";

	@Autowired
	private StringRedisTemplate redisTemplate;

    @Autowired
    private TicketService ticketService;

	@Value("${win}")
	private String win;

    @GetMapping("/sell/{id}")
    public void sell(@PathVariable("id") Integer id) throws InterruptedException {
		String key = TICKET_LOCK_PREFIX + id;
		String value = win;

		//开启锁续时（这里我们使用 守护线程-当一个程序中所有的线程都是守护线程时，守护线程会自动退出）
		Thread watchDog = new Thread(){
			@Override
			public void run() {
				while (true){
					//对我们自己获取到的锁进行续时动作
					String value = redisTemplate.opsForValue().get(key);
					if (win.equals(value)){
						//当前获取锁的进程是我自己 -- 如果锁快超时了，续时
						//获取的是一个秒值
						Long expire = redisTemplate.getExpire(key);
						if (expire <= 2){
							//重新续时为5s
							redisTemplate.expire(key,5,TimeUnit.SECONDS);
						}
					}
				}
			}
		};
		watchDog.setDaemon(true);
		//开启
		watchDog.start();


        while (true){

			//基于redis添加分布式锁，保障多进程下的共享数据操作安全

			//加锁 (锁键要和当前购买的车票的id相关)

			/*
				问题一：当前进程获取锁后，由于进程的异常退出，导致锁无法释放的问题 -- 死锁
					可以给这个锁添加超时时间

				问题二：锁的超时时间和业务的执行时间有冲突时，也会导致加锁失败
					给锁续时	（定时器 | 守护线程） -- 看门狗（Watch Dog）
						只能给自己加的锁续时--如何判断这个锁是我自己加的？？？

			 */
			Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, value,5,TimeUnit.SECONDS);

			if (lock) {
				//给锁加超时时间 （这样加时间无法保障原子性，还是会存在死锁的风险）  LUA
				//redisTemplate.expire(key,5, TimeUnit.SECONDS);

				try{
					//查询id对应的票务信息，是否有剩余票
					Ticket ticket = ticketService.getById(id);
					Integer count = ticket.getCount();
					if (count > 0) {
						System.out.println(win + "正在卖出第" + count + "张票");
						//模拟卖票时间 (模拟业务执行时间超出锁的超时时间，导致锁失败问题)
						Thread.sleep(7000);

						count = count - 1;

						//重新更新数据库的票务信息
						ticket.setCount(count);
						ticketService.updateById(ticket);
						System.out.println("剩余票数为：" + count);
					} else {
						break;
					}
				}finally {
					//释放锁
					redisTemplate.delete(key);
				}
			}
        }
    }
}
